package com.jgame.secondarymarket.domain.product.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jgame.secondarymarket.domain.product.entity.Product;
import com.jgame.secondarymarket.domain.product.entity.ProductProperties;
import com.jgame.secondarymarket.domain.product.entity.Specification;
import com.jgame.secondarymarket.domain.product.repository.ProductPropertiesRepository;
import com.jgame.secondarymarket.domain.product.repository.ProductRepository;
import com.jgame.secondarymarket.domain.product.repository.SpecificationRepository;
import com.jgame.secondarymarket.domain.product.service.ProductService;
import com.jgame.secondarymarket.infrastructure.excetpions.BussnessException;
import com.jgame.secondarymarket.interfaces.assembler.product.ProductAssembler;
import com.jgame.secondarymarket.interfaces.assembler.product.ProductPropertiesAssembler;
import com.jgame.secondarymarket.interfaces.query.page.product.PageProductQuery;


import com.jgame.secondarymarket.interfaces.query.product.UpdateProductQuery;
import com.jgame.secondarymarket.interfaces.vo.product.ProductVO;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 商品
 *
 * @title: ProductServiceImpl
 * @Author Wangsj
 * @Date: 2021/7/21
 */
@Service
@Transactional
@Slf4j
@NoArgsConstructor
public class ProductServiceImpl implements ProductService {
    @Autowired
    ProductRepository productRepository;
    @Autowired
    SpecificationRepository specificationRepository;
    @Autowired
    ProductPropertiesRepository productPropertiesRepository;

    /**
     * 保存商品信息
     *
     * @param product
     */
    @Override
    public void saveProduct(Product product) {
        log.info("insert product {}", product.getName());
        //获取规格中最低的价格作为商品价格
        product.setPrice(getMinPrice(product.getSpecifications()));
        productRepository.save(product);

        //保存商品属性
        Set<ProductProperties> properties = product.getProperties();
        properties.stream().forEach(productProperties -> {
            productProperties.setProductId(product.getId());
        });
        productPropertiesRepository.saveBatch(properties);

        //用于填充规格值
        Map<String, Long> propertiesMap = new HashMap<>();
        properties.stream().forEach(productProperties -> {
            String key = new StrBuilder(productProperties.getTage()).append(":").append(productProperties.getValue()).toString();
            if (propertiesMap.containsKey(key)) {
                throw BussnessException.throwException("商品属性值不能重复");
            }
            propertiesMap.put(key, productProperties.getId());
        });

        //规格模型数据填充、保存
        Set<Specification> specifications = product.getSpecifications();
        Map<String, Specification> isSpePresent = new HashMap<>();
        specifications.stream().forEach(specification -> {

            if (isSpePresent.containsKey(specification.getValue())) {
                //商品规格不能重复
                throw BussnessException.throwException("商品规格不能重复");

            }
            isSpePresent.put(specification.getValue(), specification);
            List<String> propertiesDetail = Arrays.asList(specification.getValue().split(","));
            List<Long> propertiesIds = new ArrayList<>();
            //填充规格模型的属性值
            propertiesDetail.stream().forEach(p -> {
                if (!propertiesMap.containsKey(p)) {
                    throw BussnessException.throwException("选择正确的商品属性");
                }
                propertiesIds.add(propertiesMap.get(p));
            });
            //对属性id排序，并组合为，分割字符串，作为唯一标识
            List<String> pidStrs = propertiesIds.stream().sorted()
                    .map(String::valueOf).collect(Collectors.toList());
            specification.setValue(pidStrs.stream().collect(Collectors.joining(",")));


            specification.setProductId(product.getId());
        });
        specificationRepository.saveBatch(specifications);
    }

    @Override
    public void update(UpdateProductQuery query, Long productId) {

        Product exit = productRepository.getProductOrThrow(productId);
        log.info("update product {}", exit.getName());

        if (StrUtil.isNotBlank(query.getName())) {
            exit.setName(query.getName());
        }
        if (StrUtil.isNotBlank(query.getImageAddress())) {
            exit.setImageAddress(query.getImageAddress());
        }
        if (StrUtil.isNotBlank(query.getDescribes())) {
            exit.setDescribes(query.getDescribes());
        }
        productRepository.updateById(exit);
    }

    @Override
    public ProductVO findById(Long productId) {
        Product product = productRepository.getProductOrThrow(productId);
        //领域对象转换
        return ProductAssembler.toProductVO(product, productPropertiesRepository.listByProductId(product.getId()));
    }

    @Override
    public void delById(Long productId) {
        Product product = productRepository.getProductOrThrow(productId);
        productRepository.delById(product.getId());
        specificationRepository.delByProductId(product.getId());
        productPropertiesRepository.deletByProductId(product.getId());
    }

    @Override
    public IPage<ProductVO> pageProduc(PageProductQuery query) {
        IPage<Product> productPage = productRepository.getPageProduct(query);
        //获取所有id
        List<Long> productIds = productPage.getRecords().stream().map(Product::getId).collect(Collectors.toList());

        //根据商品id分组map
        Map<Long, List<ProductProperties>> propertiesMap = getPropertiesMap(productIds);


        //商品领域模型转换为VO
        List<ProductVO> productVOS = productPage.getRecords().stream().map(record -> {
            ProductVO productVO = new ProductVO();
            BeanUtil.copyProperties(record, productVO);
            if (query.isOpenDetail() && propertiesMap.containsKey(record.getId())) {
                //规格领域模型转为VO
                productVO.setProperties(ProductPropertiesAssembler.toVO(propertiesMap.get(record.getId())));
            }
            log.info(productVO.toString());
            return productVO;

        }).collect(Collectors.toList());
        IPage<ProductVO> result = new Page<>();
        BeanUtil.copyProperties(productPage, result);
        result.setRecords(productVOS);
        return result;
    }

    /**
     * 商品属性map key:productId
     *
     * @param productIds
     * @return
     */
    private Map<Long, List<ProductProperties>> getPropertiesMap(List<Long> productIds) {
        Map<Long, List<ProductProperties>> result = new HashMap<>();
        if (CollectionUtil.isNotEmpty(productIds)) {
            result = productPropertiesRepository.listByProductId(productIds).stream()
                    .collect(Collectors.groupingBy(ProductProperties::getProductId));
        } else {
            log.warn("productIds are empty!!");
        }
        return result;
    }

    /**
     * 获取所有规格价格中最低的价格
     *
     * @param specifications
     * @return
     */
    private BigDecimal getMinPrice(Set<Specification> specifications) {
        return specifications.stream()
                .map(Specification::getPrice)
                .min(BigDecimal::compareTo)
                .orElseThrow(() -> BussnessException.throwException("商品价格不能为空"));
    }


}
